----------------------------------------------------------------------------------
-- Project 2, Part 2
-- Jason Parrott
-- Rodney McGee
--
-- Main loop
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity program is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           keyboard_ascii : in  STD_LOGIC_VECTOR (7 downto 0);
           keyboard_ready : in  STD_LOGIC;
           ascii_code : out  STD_LOGIC_VECTOR (7 downto 0);
           lcd_data_ready : out  STD_LOGIC;
			  leds : out STD_LOGIC_VECTOR (7 downto 0);
	   tx : out STD_LOGIC;
	   rx : in STD_LOGIC
	);
end program;

architecture Behavioral of program is


-- component program_tx is
--     Port ( data_in : in  STD_LOGIC_VECTOR (127 downto 0);
--            clk : in  STD_LOGIC;
--            reset : in  STD_LOGIC;
--            w_data : out  STD_LOGIC_VECTOR (7 downto 0);
--            tx_full : in  STD_LOGIC;
--            wr_uart : out  STD_LOGIC;
--            data_available : in  STD_LOGIC;
-- 	   data_ack : out STD_LOGIC);
-- end component;

component uart is
   generic(
     -- Default setting:
     -- 19,200 baud, 8 data bis, 1 stop its, 2^2 FIFO
     DBIT: integer:=8;     -- # data bits
      SB_TICK: integer:=16; -- # ticks for stop bits, 16/24/32
                            --   for 1/1.5/2 stop bits
      DVSR: integer:= 163;  -- baud rate divisor
                            -- DVSR = 50M/(16*baud rate)
      DVSR_BIT: integer:=8; -- # bits of DVSR
      FIFO_W: integer:=2    -- # addr bits of FIFO
                            -- # words in FIFO=2^FIFO_W
   );
   port(
      clk, reset: in std_logic;
      rd_uart, wr_uart: in std_logic;
      rx: in std_logic;
      w_data: in std_logic_vector(7 downto 0);
      tx_full, rx_empty: out std_logic;
      r_data: out std_logic_vector(7 downto 0);
      tx: out std_logic
   );
end component;

signal wait_counter : STD_LOGIC_VECTOR (26 downto 0);  --2 sec - 101111101011110000100000000
signal shortcounter : STD_LOGIC_VECTOR (24 downto 0);  --.5 sec - 1011111010111100001000000
signal tinycounter : STD_LOGIC_VECTOR (10 downto 0);
signal allow_keyboard : STD_LOGIC;

signal welcome_message : STD_LOGIC_VECTOR (127 downto 0);
signal password_message : STD_LOGIC_VECTOR (127 downto 0);
signal accessgranted_message : STD_LOGIC_VECTOR (127 downto 0);
signal accessdenied_message : STD_LOGIC_VECTOR (127 downto 0);
signal encode_message : STD_LOGIC_VECTOR (127 downto 0);
signal decode_message : STD_LOGIC_VECTOR (127 downto 0);
signal message_buffer : STD_LOGIC_VECTOR (127 downto 0);
signal reprint_buffer : STD_LOGIC_VECTOR (127 downto 0);

signal string_buffer : STD_LOGIC_VECTOR (127 downto 0);
signal receive_buffer : STD_LOGIC_VECTOR (127 downto 0);

signal uart_input : STD_LOGIC_VECTOR (127 downto 0);
signal uart_output : STD_LOGIC_VECTOR (127 downto 0);
signal uart_input_ready : STD_LOGIC;
signal uart_output_ready : STD_LOGIC;
signal uart_input_ack : STD_LOGIC;
signal uart_output_ack : STD_LOGIC;



signal tx_full, rx_empty, rd_uart, wr_uart : STD_LOGIC;
signal tx_buf : STD_LOGIC;
signal w_data, r_data : STD_LOGIC_VECTOR (7 downto 0);

signal led_bank: STD_LOGIC_VECTOR (7 downto 0);

type statetype is (	S1, S11, S12, S13, S14, S15, S16,                -- Print out welcome message
							S4, S41, S42, S43, S47, S48,      -- Encrypt/Decrypt Prompt
							S5, S51, --S52, -- Display crypto result
							--S5, S51, S52, S53, S54, S55, S56, S57, S58, S59, S5A, -- Display crypto result
							--S6, S61,                                         -- Pause for keyboard
							S7, S71, S72, S73, S74, S75, S76, S77, S78,
							S99);                              
signal state, next_state : statetype := S1;

begin

   --uart_instance: entity work.uart(arch)
   uart_instance: entity uart
--      generic map(DVSR => 50000000, DVSR_BIT => 26)
      port map(clk=>clk, reset=>reset, rd_uart=> rd_uart,
               wr_uart=>wr_uart, rx=>rx, w_data=>w_data,
               tx_full=>tx_full, rx_empty=>rx_empty,
               r_data=>r_data, tx=>tx_buf);
	       --r_data=>r_data, tx=>leds(0));

   program_transmitter: entity work.program_tx(arch)
	port map(clk => clk, reset => reset, w_data => w_data, wr_uart => wr_uart, tx_full => tx_full, data_in => uart_input, data_available => uart_input_ready, data_ack => uart_input_ack );

	program_reciever: entity program_rx
	port map(clk, reset, uart_output, uart_output_ready, uart_output_ack, rd_uart, rx_empty, r_data); 

-- Port mappings for addtional modules needed within the program loop

state <= next_state;

leds <= led_bank;

process (clk, reset) is
variable string_counter: std_logic_vector (7 downto 0) := "00000000";
variable line_counter: std_logic_vector (5 downto 0) := "000000";
begin
		 if (reset = '1') then
					next_state <= S1;
					string_counter := "00000000";
					lcd_data_ready <= '0';
					
					allow_keyboard <= '0';
					line_counter := "000000";
					
					wait_counter <= "000000000000000000000000000";
					shortcounter <= "0000000000000000000000000";
					tinycounter <= "00000000000";
					
					welcome_message       <= x"57656c636f6d65204350454734323221";
					password_message      <= x"456e7465722050617373776f72643a20";
					accessgranted_message <= x"416363657373204772616e7465642120";
					accessdenied_message  <= x"4163636573732044656e696564203d28";
					decode_message        <= x"5465787420746f206465636f64653a20";
					encode_message        <= x"5465787420746f20656e636f64653a20";
					
					receive_buffer			 <= x"20202020202020202020202020202020";
					string_buffer			 <= x"00000000000000000000000000000000";
					
					
					uart_input			 <= x"00000000000000000000000000000000";
					uart_input_ready 	 <= '0';
		 elsif (rising_edge(clk)) then
--			leds <= std_logic_vector(line_counter) & "00";
					case state is
					
					-- ===============================================
					-- State 1: Print Welcome Message
					-- ===============================================
					
					-- Wait .5 seconds
					when S1 =>
						led_bank <= x"10";
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S11;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S1;
						end if;
							  
					-- Print out welcome message
					when S11 =>
						led_bank <= x"11";
						ascii_code <= welcome_message(127 downto 120);
						lcd_data_ready <= '1';
						
						welcome_message <= welcome_message(119 downto 0) & "00000000";
						string_counter := string_counter + 8;
						next_state <= S12;
						
					when S12 =>
						led_bank <= x"12";
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								next_state <= S13;
							else
								next_state <= S11;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S12;
						end if;
							  
					-- Wait 2 seconds
					when S13 =>
						led_bank <= x"13";
						if(wait_counter = "101111101011110000100000000") then
							wait_counter <= "000000000000000000000000000";
							next_state <= S14;
						else
							wait_counter <= wait_counter + 1;
							next_state <= S13;
						end if;
						
					-- Erase screen
					when S14 =>
						led_bank <= x"14";
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S15;
						
					when S15 =>
						led_bank <= x"15";
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S16;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S15;
						end if;
						
					-- Wait .5 seconds
					when S16 =>
						led_bank <= x"16";
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S4;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S16;
						end if;
						
					-- ===============================================
					-- State 4: Encode or Decode Input
					-- ===============================================
													  
					-- Wait 2 seconds
					when S4 =>
						led_bank <= x"40";
						if(wait_counter = "101111101011110000100000000") then
							wait_counter <= "000000000000000000000000000";
							next_state <= S41;
						else
							wait_counter <= wait_counter + 1;
							next_state <= S4;
						end if;
						
					-- Erase screen
					when S41 =>
						led_bank <= x"41";
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S42;
						
					when S42 =>
						led_bank <= x"42";
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S43;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S42;
						end if;
						
					-- Wait .5 seconds
					when S43 =>
						led_bank <= x"43";
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							allow_keyboard <= '1';
							next_state <= S7;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S43;
						end if;
						
					-- Print out ENCODE/DECODE Prompt
--					when S45 =>
--						ascii_code <= message_buffer(127 downto 120);
--						lcd_data_ready <= '1';
--						
--						message_buffer <= message_buffer(119 downto 0) & "00000000";
--						string_counter := string_counter + 8;
--						next_state <= S46;
--						
--					when S46 =>
--						if(tinycounter = "11111111111") then
--							tinycounter <= "00000000000";
--							lcd_data_ready <= '0';
--							if(string_counter = x"80") then
--								string_counter := "00000000";
--								allow_keyboard <= '1';
--								next_state <= S47;
--							else
--								next_state <= S45;
--							end if;
--						else
--							tinycounter <= tinycounter + 1;
--							next_state <= S46;
--						end if;
							  
					-- Process keyboard input
					when S47 =>
						led_bank <= x"47";
						allow_keyboard <= '1';
						if(uart_output_ready = '1') then
							receive_buffer <= uart_output;
							uart_output_ack <= '1';
							next_state <= S7;
						elsif(keyboard_ready = '1' and allow_keyboard = '1') then
							if(keyboard_ascii = x"0D") then    -- Enter/Return
								line_counter := "000000";
								allow_keyboard <= '0';
								next_state <= S5;
							elsif(keyboard_ascii = x"08") then  -- Backspace
								if line_counter /= "000000" then
									string_buffer <= x"00" & string_buffer(127 downto 8);
									line_counter := line_counter - 1;
									
									ascii_code <= keyboard_ascii; -- Put backspace to lcd to 'backup' cursor
									lcd_data_ready <= '1';
									next_state <= S48;
								else
									next_state <= S47;
								end if;
							elsif(keyboard_ascii = x"00") then  -- Unknown
								next_state <= S47;
							else
								if line_counter /= "010000" then
									string_buffer <= string_buffer(119 downto 0) & keyboard_ascii;
									
									ascii_code <= keyboard_ascii;
									line_counter := line_counter + 1;
									lcd_data_ready <= '1';
									next_state <= S48;
								else
									next_state <= S47;
								end if;
							end if;
						else
							lcd_data_ready <= '0';
							next_state <= S47;
						end if;
							  
					when S48 =>
						led_bank <= x"48";
						lcd_data_ready <= '0';
						if line_counter = "010000" then
							line_counter := "000000";
							allow_keyboard <= '0';
							next_state <= S5;
						else
							next_state <= S47;
						end if;
						
						
					-- Send our string to the uart
					when S5 =>
						led_bank <= x"50";
						uart_input <= string_buffer;
						uart_input_ready <= '1';
						next_state <= S51;
						string_counter := x"10";


					when S51 =>
						if(string_counter /= line_counter) then
							
					when S51 =>
						led_bank <= x"51";
						uart_input_ready <= '0';
						if(uart_input_ack = '0') then
							next_state <= S51;
						else
							string_buffer <= x"00000000000000000000000000000000";
							next_state <= S7;
						end if;
						
						
					-- Wait for uart to be finished
--					when S52 =>
--						led_bank <= x"52";
--						if(uart_output_ready = '1') then
--							next_state <= S47;
--						else
--							next_state <= S52;
--						end if;
						
					-- Wait .5 seconds
--					when S58 =>
--						led_bank <= x"58";
--						if(shortcounter = "1011111010111100001000000") then
--							shortcounter <= "0000000000000000000000000";
--							next_state <= S59;
--						else
--							shortcounter <= shortcounter + 1;
--							next_state <= S58;
--						end if;
						
											
					-- ===============================================
					-- State 6: Stay in this state - Wait for keyboard
					-- ===============================================
--					when S6 =>
--						led_bank <= x"60";
--						if (keyboard_ready = '1') then
--							if (allow_keyboard = '1') then
--								next_state <= S61;
--							end if;
--						else
--							next_state <= S6;
--						end if;
--							  
--					when S61 =>
--						led_bank <= x"61";
--						allow_keyboard <= '0';
--						next_state <= S41;
						
					

					-- ===============================================
					-- State 7: print the screen when we receive data
					-- ===============================================
					
					-- clear screen
					when S7 =>
						led_bank <= x"70";
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S71;
						reprint_buffer <= receive_buffer;


					when S71 =>
						led_bank <= x"71";
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S78;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S71;
						end if;

					-- Wait .5 seconds
					when S78 =>
						led_bank <= x"78";
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S72;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S78;
						end if;
					
					-- print newly recieved message
					when S72 =>
						led_bank <= x"72";
						ascii_code <= reprint_buffer(127 downto 120);
						lcd_data_ready <= '1';

						reprint_buffer <= reprint_buffer(119 downto 0) & x"20";
						string_counter := string_counter + 8;
						next_state <= S73;

					when S73 =>
						led_bank <= x"73";
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								--allow_keyboard <= '1';
								next_state <= S74;
							else
								next_state <= S72;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S73;
						end if;
					
					-- reprint user input
					when S74 =>
						led_bank <= x"74";
						if(line_counter = "000000") then
							next_state <= S47;
						else
							reprint_buffer <= string_buffer;
							next_state <= S75;
						end if;

					when S75 =>
						led_bank <= x"75";
						if( reprint_buffer(127 downto 120) /= x"00" ) then
							next_state <= S76;
						else
							reprint_buffer <= reprint_buffer(119 downto 0) & x"00";
						end if;

					when S76 =>
						led_bank <= x"76";
						ascii_code <= reprint_buffer(127 downto 120);
						lcd_data_ready <= '1';
						reprint_buffer <= reprint_buffer(119 downto 0) & x"00";
						next_state <= S77;

					when S77 =>
						led_bank <= x"77";
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(reprint_buffer(127 downto 120) = x"00") then
								allow_keyboard <= '1';
								next_state <= S47;
							else
								next_state <= S76;
							end if;
						else
							tinycounter <= tinycounter + 1;
						end if;
							
					-- ===============================================
					-- State 99: Infinite Loop
					-- ===============================================
					when S99 =>
						led_bank <= x"99";
						next_state <= S99;	
						
						
						
					end case;
		 end if;
end process;


end Behavioral;

